home *** CD-ROM | disk | FTP | other *** search
- /*
- File: EchoModule.c
-
- Contains: Sample Stream Driver that echos data
-
- Sort of the minimal STREAMS driver - is neither TPI nor DLPI compliant
- */
-
- #ifndef __OPENTPTMODULE__
- #include <OpenTptModule.h>
- #endif
- #ifndef _MPS_STROPTS_
- #include <stropts.h>
- #endif
-
- /*
- * Forward function declarations
- */
-
- int echo_admin(void);
- int echo_close(queue_t*, int, cred_t*);
- int echo_open(queue_t*, dev_t*, int, int, cred_t*);
- int echo_rsrv(queue_t*);
- int echo_wput(queue_t*, mblk_t*);
-
- /*
- * Module information for the streamtab structure
- */
-
- static struct module_info minfo =
- {
- 9000, /* Module Number */
- "SmplEcho", /* Name of module */
- 0, /* Minimum data size */
- INFPSZ, /* Maximum data size */
- 2048, /* Hi water mark for queue */
- 128 /* Lo water mark for queue */
- };
-
- /*
- * Info for the "read" queue.
- */
-
- static struct qinit rinit =
- {
- putq, /* Always "schedule" on the read_side */
- echo_rsrv, /* Service routine for "incoming" data */
- echo_open, /* Our open routine */
- echo_close, /* Our close routine */
- echo_admin, /* Our admin routine */
- &minfo /* Our module_info */
- };
-
- /*
- * Info for the "write" queue
- */
-
- static struct qinit winit =
- {
- echo_wput, /* Our write-side "put" routine */
- 0, /* No service routine on the write_side queue */
- 0, /* open routine goes in read-side structure */
- 0, /* close routine goes in read-side structure */
- 0, /* admin routine goes in read-side structure */
- &minfo /* Our module_info */
- };
-
- /*
- * The streamtab structure
- */
-
- static struct streamtab theStreamTab =
- {
- &rinit, /* Our read-side qinit structure */
- &winit, /* Our write-side qinit structure */
- 0, /* Not a mux - no structure info */
- 0 /* Not a mux - no structure info */
- };
-
- static UInt8 gMinorNums[256];
-
- /*******************************************************************************
- ** Open routine
- ********************************************************************************/
-
- int echo_open(queue_t* rdq, dev_t* dev, int flag, int sflag, cred_t* creds)
- {
- #pragma unused (flag)
- int err;
- size_t idx;
- /*
- * Make sure caller is properly credentialed
- */
- if ( (err = drv_priv(creds)) != kOTNoError )
- return err;
- /*
- * If we're being reopened - just return
- */
- if ( rdq->q_ptr != NULL )
- return kOTNoError;
- /*
- * Make sure we're being opened properly - We only allow
- * a "clone" open.
- */
- if ( sflag != CLONEOPEN )
- return ENXIO;
- /*
- * Find a minor number we can use
- */
- for (idx = 0; idx < sizeof(gMinorNums); ++idx)
- if ( gMinorNums[idx] == 0 )
- break;
- /*
- * If we're out of minor numbers - return an error
- */
- if ( idx >= sizeof(gMinorNums) )
- return ENXIO;
- gMinorNums[idx] = 1;
- /*
- * Store our minor number in the q_ptr field (which is ours
- * to do with as we please). Normally, we would store some
- * instance data there, but echo doesn't need any.
- */
- rdq->q_ptr = (void*)(idx + kFirstMinorNumber);
- WR(rdq)->q_ptr = rdq->q_ptr;
- /*
- * Set our device number so STREAMS knows what we are
- */
- *dev = makedev(getmajor(*dev), idx + kFirstMinorNumber);
- /*
- * return no error
- */
- return kOTNoError;
- }
-
- /*******************************************************************************
- ** Close routine
- ********************************************************************************/
-
- int echo_close(queue_t* rdq, int flags, cred_t* credP)
- {
- /*
- * Free my minor number.
- * Zero out the q_ptr. If we allocated any memory and stored
- * it there, now would be a good time to free it!
- */
- gMinorNums[(UInt32)(rdq->q_ptr) - kFirstMinorNumber] = 0;
- rdq->q_ptr = NULL;
- WR(rdq)->q_ptr = NULL;
- /*
- * Return no error
- */
- return kOTNoError;
- }
-
- /*******************************************************************************
- ** Admin routine
- **
- ** Admin routines are for future expansion - just return no error.
- ********************************************************************************/
-
- int echo_admin()
- {
- return kOTNoError;
- }
-
- /*******************************************************************************
- ** Write-side put routine
- ********************************************************************************/
-
- int echo_wput(queue_t* q, mblk_t* mp)
- {
- /*
- * Process the incoming message
- */
- switch (mp->b_datap->db_type)
- {
- /*
- * These messages can't come from upstream
- */
- case M_COPYIN:
- case M_COPYOUT:
- case M_ERROR:
- case M_HANGUP:
- case M_IOCACK:
- case M_IOCNAK:
- case M_SETOPTS:
- case M_SIG:
- break;
- /*
- * Process a signal message - Under Open Transport, this is normally
- * a timer message
- */
- case M_PCSIG:
- break;
- /*
- * Undocumented message type
- */
- case M_HPDATA:
- break;
- /*
- * You will get one of these if you use M_COPYIN.
- */
- case M_IOCDATA:
- break;
- /*
- * These are messages for future expansion. STREAMS says if you
- * don't understand the message, free it if you are a driver.
- */
- case M_PCRSE:
- case M_RSE:
- break;
-
- /*
- * Request to flush queues. We're a driver, so we have to do a little
- * more work than other modules when we receive one.
- */
- case M_FLUSH:
- if ( mp->b_rptr[0] & FLUSHW)
- {
- if ( (mp->b_rptr[0] & FLUSHBAND) && mp->b_wptr - mp->b_rptr == 2 )
- flushband(q, mp->b_rptr[1], FLUSHALL);
- else
- flushq(q, FLUSHALL);
- }
- if ( mp->b_rptr[0] & FLUSHR )
- {
- if ( (mp->b_rptr[0] & FLUSHBAND) && mp->b_wptr - mp->b_rptr == 2 )
- flushband(RD(q), mp->b_rptr[1], FLUSHALL);
- else
- flushq(RD(q), FLUSHALL);
- mp->b_rptr[0] &= ~FLUSHW;
- qreply(q, mp);
- return 0;
- }
- break;
-
- /*
- * These messages are rarely uses, but you may need to process them
- */
- case M_STOP:
- case M_START:
- case M_STOPI:
- case M_STARTI:
- case M_READ:
- case M_BREAK:
- case M_DELAY:
- case M_CTL:
- break;
-
- /*
- * As a driver, it is our responsibility to NAK any IOCTL that we receive
- * that we don't understand. If we were a module, we would just pass it on.
- */
- case M_IOCTL:
- {
- struct iocblk* iocp = (struct iocblk*)mp->b_rptr;
- iocp->ioc_error = EINVAL;
- iocp->ioc_count = 0;
- mp->b_datap->db_type = M_IOCNAK;
- qreply(q, mp);
- return 0;
- }
-
- /*
- * We're an echo module, so just send the data back up the STREAM if we can.
- * If we're flow controlled - put it on the queue for later processing.
- */
- case M_DATA:
- case M_PROTO:
- case M_PCPROTO:
- /*
- * If we are not flow controlled, or it is a hi-priority message,
- * send it back up the read-side queue.
- * Otherwise, do a putq on the read queue to schedule the
- * data for being sent upstream when flow control lifts.
- */
- if ( mp->b_datap->db_type > QPCTL || bcanput(RD(q)->q_next, mp->b_band) )
- qreply(q, mp);
- else
- putq(RD(q), mp);
- return 0;
-
- /*
- * Should never see one of these messages. It is an invisible message
- * used by the streamhead.
- */
- case M_PASSFP:
- break;
-
- default:
- break;
- }
- freemsg(mp);
- return 0;
- }
-
- /*******************************************************************************
- ** Read-side service routine
- **
- ** Data to be sent upstream was put on the read-side queue, if flow control
- ** was on. When flow control is lifted, this routine will be scheduled to run.
- ********************************************************************************/
-
- int echo_rsrv(queue_t* q)
- {
- mblk_t* mp;
- /*
- * We know that only M_PROTO and M_DATA messages are on the queue, since that
- * is all that we scheduled.
- */
- while ( (mp = getq(q)) != NULL )
- {
- /*
- * If we're flow controlled, get out and we'll come back when the flow-
- * control lifts.
- */
- if ( !bcanput(q->q_next, mp->b_band) )
- {
- putbq(q, mp);
- return 0;
- }
- /*
- * Otherwise, send the data upstream, and get some more
- */
- putnext(q, mp);
- }
- }
-
-
- /* ------------------------------------------------------------------------
-
- THESE FUNCTIONS AND DATA STRUCTURES ARE SPECIFIC TO THE MACINTOSH IMPLEMENTATION
- OF STREAMS. THESE ROUTINES WOULD NOT EXIST IN OTHER STREAMS ENVIRONMENTS.
-
- WHEN PORTING OTHER STREAMS CODE TO THE MACINTOSH, THESE ROUTINES AND DATA
- STRUCTURES MUST BE ADDED.
-
- ------------------------------------------------------------------------ */
-
- /*******************************************************************************
- ** install_info structure, and the GetInstallInfo function
- **
- ** This is the structure that tells Open Transport about your module. OT calls
- ** your "GetInstallInfo" function to get this information.
- **
- ********************************************************************************/
-
- static struct install_info theInstallInfo =
- {
- &theStreamTab, // Stream Tab pointer
- kOTModIsDriver, // Tell OT we are a driver
- SQLVL_MODULE, // Synchronization level - the module will never
- // be re-entered with this setting
- 0, // Shared writer list buddy
- 0, // Open Transport use - always set to 0
- 0 // Flag - always set to 0
- };
-
- install_info* GetOTInstallInfo()
- {
- return &theInstallInfo;
- }
-
- /*******************************************************************************
- ** This is the function that will be called the first time that this code
- ** is loaded. It is guaranteed that this function is call at SystemTask time
- ** on the Macintosh.
- ********************************************************************************/
-
- Boolean InitStreamModule(void* cookie)
- {
- BZERO(gMinorNums, sizeof(gMinorNums));
- return true;
- }
-
-